package org.testis.orm.refinedclasses;

import java.util.HashMap;
import java.util.LinkedList;
import org.orm.PersistentException;
import org.testis.orm.classes.T_bed_firmware;
import org.testis.orm.classes.T_dmap_designtree;
import org.testis.orm.classes.T_dmap_objects;
import org.testis.orm.dynamicmap.validation.IValidateDynamicMapNode;
import org.testis.orm.dynamicmap.validation.ValidationFactory;
import org.testis.utils.tree.TreeNode;

/**
 * 
 * @author Cahit Emir Erel
 */
public class DynamicMapDesignTreeNode extends TreeNode implements Comparable {

    private T_dmap_designtree m_reference;
    private static HashMap<Integer, DynamicMapDesignTreeNode> m_nodeList = new HashMap<Integer, DynamicMapDesignTreeNode>();
    private StringBuilder m_errors = new StringBuilder();

    /**
     * Creates an empty DynamicMapDesignTreeNode object to be used as root
     */
    private DynamicMapDesignTreeNode() {
	super();
	initialize();

    }

    /**
     * Creates a DynamicMapDesignTreeNode object with given Design Tree
     * reference
     * 
     * @param tree
     *            - Design Tree reference
     * @throws NullPointerException
     * @throws InstantiationError
     */
    private DynamicMapDesignTreeNode(T_dmap_designtree reference) {
	this();
	setReference(reference);
	T_dmap_objects object;
	if (isClone()) {
	    object = reference.getClone().getObject();
	} else {
	    object = reference.getObject();
	}
	if (object == null) {
	    throw new InstantiationError(
		    "Referenced tree has no object reference!");
	}
	setObject(new DynamicMapObject(object));
    }

    public static DynamicMapDesignTreeNode createEmptyNode() {
	return new DynamicMapDesignTreeNode();
    }

    public static DynamicMapDesignTreeNode getNode(T_dmap_designtree reference) {
	if (m_nodeList.containsKey(reference.getId())) {
	    DynamicMapDesignTreeNode node = m_nodeList.get(reference.getId());
	    return node;
	}
	DynamicMapDesignTreeNode node = new DynamicMapDesignTreeNode(reference);
	m_nodeList.put(reference.getId(), node);
	return node;
    }

    @Override
    public void setPlace(Integer place) {
	super.setPlace(place);
    }

    /**
     * Initializes necessary variables
     */
    private void initialize() {
    }

    /**
     * Returns all Design Tree references of clones of this object's Design Tree
     * reference
     * 
     * @return all Design Tree references of clones of this object's Design Tree
     *         reference
     */
    synchronized public T_dmap_designtree[] getClones() {
	return getClones(getReference());
    }

    /**
     * Returns all Design Tree references of clones of the given Design Tree
     * reference
     * 
     * @return all Design Tree references of clones of the given Design Tree
     *         reference
     * @throws NullPointerException
     */
    synchronized private static T_dmap_designtree[] getClones(
	    T_dmap_designtree temp) {
	if (temp.t_dmap_designtree1 != null
		&& temp.t_dmap_designtree1.size() > 0) {
	    return temp.t_dmap_designtree1.toArray();
	} else {
	    return new T_dmap_designtree[0];
	}
    }

    @Override
    public Integer getPlace() {
	if (this.getParent() != null) {
	    return this.getParent().getChildIndex(this);
	} else {
	    return -1;
	}
    }

    /**
     * Checks if this Design Tree Node is a ghost
     * 
     * @return true if this Design Tree Node is a ghost
     */
    public boolean isGhost() {
	if (getReference().getParent() == null
		&& getReference().getRank() == null
		&& !getObject().getTypeName().equals("UIDeviceMapContainer")) {
	    return true;
	} else {
	    return false;
	}
    }

    /**
     * Return the nth child of this Design Tree Node
     * 
     * @param n
     *            - place of the child in the Children list
     * @return nth child of this Design Tree Node
     */
    @Override
    public DynamicMapDesignTreeNode getChild(int n) {
	return (DynamicMapDesignTreeNode) super.getChild(n);
    }

    /**
     * Returns a database copy of this object
     * 
     * @return a database copy of this object
     */
    synchronized public DynamicMapDesignTreeNode createRefinedDatabaseCopy(
	    T_dmap_designtree parentTreeNode, T_bed_firmware firmware) {
	try {
	    return new DynamicMapDesignTreeNode(createDatabaseCopy(
		    parentTreeNode, firmware));
	} catch (PersistentException ex) {
	    ex.printStackTrace();
	    return null;
	}
    }

    /**
     * Returns a database copy of the Design Tree reference
     * 
     * @return a database copy of the Design Tree reference
     * @throws org.orm.PersistentException
     */
    private T_dmap_designtree createDatabaseCopy(
	    T_dmap_designtree parentTreeNode, T_bed_firmware firmware)
	    throws PersistentException {
	try {
	    DynamicMapObject newObject = this.getObject()
		    .createRefinedDatabaseCopy();
	    if (newObject == null) {
		throw new PersistentException();
	    }
	    T_dmap_designtree newTree = new T_dmap_designtree();
	    newTree.setClone(null);
	    newTree.setFirmware(firmware);
	    newTree.setParent(parentTreeNode);
	    newTree.setObject(newObject.getReference());
	    if (newTree.save()) {
		return newTree;
	    } else {
		throw new PersistentException();
	    }
	} catch (PersistentException ex) {
	    throw new PersistentException("Unable to save T_dmap_designtree");
	}
    }

    public boolean isClone() {
	if (getReference().getClone() == null) {
	    return false;
	} else {
	    return true;
	}
    }

    @Override
    public DynamicMapDesignTreeNode[] getChildren() {
	return super.getChildrenLinkedList().toArray(
		new DynamicMapDesignTreeNode[getChildrenLinkedList().size()]);
    }

    public LinkedList<DynamicMapDesignTreeNode> getChildrenAsList() {
	DynamicMapDesignTreeNode[] nodes = getChildren();
	LinkedList<DynamicMapDesignTreeNode> nodeList = new LinkedList<DynamicMapDesignTreeNode>();
	HashMap<Integer, String> map = new HashMap<Integer, String>();
	for (int i = 0; i < nodes.length; i++) {
	    T_dmap_designtree dtree = nodes[i].getReference();
	    if (!map.containsKey(dtree.getId())) {
		map.put(dtree.getId(), "");
		nodeList.add(getNode(dtree));
	    }
	}

	return nodeList;

    }

    public int getChildrenCount() {
	if (super.getChildrenLinkedList() == null) {
	    return 0;
	}
	return super.getChildrenLinkedList().size();
    }

    @Override
    public DynamicMapObject getObject() {
	return (DynamicMapObject) super.getObject();
    }

    public DynamicMapDesignTreeNode searchObjectById(int id) {
	if (getObject().getId() == id) {
	    return this;
	}
	DynamicMapDesignTreeNode result;
	TreeNode[] list = super.getChildren();
	if (list != null) {
	    for (int i = 0; i < list.length; i++) {
		result = ((DynamicMapDesignTreeNode) list[i])
			.searchObjectById(id);
		if (result != null) {
		    return result;
		}
	    }
	}
	return null;
    }

    /**
     * 
     * @param parameterName
     * @param parameterValue
     * @return
     */
    public DynamicMapDesignTreeNode searchObject(String parameterName,
	    String parameterValue) {

	DynamicMapParameter parameter = getObject().getParameterByName(
		parameterName);
	if (parameter != null) {
	    if (parameter.getReference().getValue().equals(parameterValue)) {
		return this;
	    }
	}
	TreeNode[] list = super.getChildren();
	for (int i = 0; i < list.length; i++) {
	    DynamicMapDesignTreeNode result = ((DynamicMapDesignTreeNode) list[i])
		    .searchObject(parameterName, parameterValue);
	    if (result != null) {
		return result;
	    }
	}
	return null;
    }

    public void searchObjects(String parameterName, String parameterValue,
	    LinkedList<DynamicMapDesignTreeNode> output) {

	DynamicMapParameter parameter = getObject().getParameterByName(
		parameterName);
	if (parameter != null) {
	    if (parameter.getReference().getValue().equals(parameterValue)) {
		output.add(this);
		return;
	    }
	}
	TreeNode[] list = super.getChildren();
	DynamicMapDesignTreeNode child;
	for (int i = 0; i < list.length; i++) {
	    child = ((DynamicMapDesignTreeNode) list[i]);
	    child.searchObjects(parameterName, parameterValue, output);
	}
	return;
    }

    /**
     * Returns the TreeNode in the hierarchy that meets the given criteria
     * 
     * @param parameterName
     *            - parameter name
     * @param parameterValue
     *            - parameter value
     * @param objectType
     *            - type of the dynamic map object
     * @return TreeNode in the hierarchy that meets the given criteria
     * @throws NullPointerException
     */
    public DynamicMapDesignTreeNode searchObjectByParameterValue(
	    String parameterName, String parameterValue, String objectType) {
	if (getObject().getTypeName().equals(objectType)) {
	    DynamicMapParameter parameter = getObject().getParameterByName(
		    parameterName);
	    if (parameter != null) {
		if (parameter.getReference().getValue().equals(parameterValue)) {
		    return this;
		}
	    }
	}

	TreeNode[] list = super.getChildren();
	DynamicMapDesignTreeNode child = null;

	for (int i = 0; i < list.length; i++) {
	    child = ((DynamicMapDesignTreeNode) list[i]);
	    DynamicMapDesignTreeNode result = child
		    .searchObjectByParameterValue(parameterName,
			    parameterValue, objectType);
	    if (result != null) {
		return result;
	    }
	}
	return null;
    }

    /**
     * Returns the TreeNode in the hierarchy that meets the given criteria
     * 
     * @param parameterName
     *            - parameter name
     * @param parameterValue
     *            - parameter value
     * @param objectType
     *            - type of the dynamic map object
     * @return TreeNode in the hierarchy that meets the given criteria
     * @throws NullPointerException
     */
    public void searchObjectsByParameterValue(String parameterName,
	    String parameterValue, String objectType,
	    LinkedList<DynamicMapDesignTreeNode> output) {
	if (getObject().getTypeName().equals(objectType)) {
	    DynamicMapParameter parameter = getObject().getParameterByName(
		    parameterName);
	    if (parameter != null) {
		if (parameter.getReference().getValue().equals(parameterValue)) {
		    output.add(this);
		    return;
		}
	    }
	}

	TreeNode[] list = super.getChildren();
	DynamicMapDesignTreeNode child = null;

	for (int i = 0; i < list.length; i++) {
	    child = ((DynamicMapDesignTreeNode) list[i]);
	    child.searchObjectsByParameterValue(parameterName, parameterValue,
		    objectType, output);

	}
	return;
    }

    public void searchObjectsByType(String objectType,
	    LinkedList<DynamicMapDesignTreeNode> output) {
	if (getObject().getTypeName().equals(objectType)) {
	    output.add(this);
	    return;
	}

	TreeNode[] list = super.getChildren();
	DynamicMapDesignTreeNode child = null;

	for (int i = 0; i < list.length; i++) {
	    child = ((DynamicMapDesignTreeNode) list[i]);
	    child.searchObjectsByType(objectType, output);

	}
	return;
    }

    public void setObject(DynamicMapObject object) {
	if (object == null) {
	    throw new NullPointerException();
	}
	super.setObject(object);
	if (object != null) {
	    super.setName("" + object.getName());
	} else {
	    super.setName(null);
	}
    }

    public T_dmap_designtree getReference() {
	return m_reference;
    }

    /**
     * Sets the Design Tree reference
     * 
     * @param reference
     *            - Design Tree reference
     * @throws NullPointerException
     */
    private void setReference(T_dmap_designtree reference) {
	if (reference == null) {
	    throw new NullPointerException();
	}
	m_reference = reference;
    }

    public int getId() {
	return getReference().getId();
    }

    public void addChild(DynamicMapDesignTreeNode node) {
	super.addChild(node);
	node.setParent(this);
    }

    public void addChild(int n, DynamicMapDesignTreeNode node) {
	super.addChild(n, node);
	node.setParent(this);
    }

    public boolean delete() {

	boolean result = true;
	try {
	    DynamicMapDesignTreeNode next = getNextSibling();
	    if (next != null) {
		next.getReference().setRank(getReference().getRank());
		if (!next.getReference().save()) {
		    return false;
		}
	    }
	    if (isSafeToDelete()) {
		T_dmap_designtree clonedTree = null;
		if (isClone()) {
		    clonedTree = getReference().getClone();
		}
		if (getReference().deleteAndDissociate()) {
		    if (clonedTree == null) {
			result = getObject().delete();
		    } else {
			if (clonedTree.getParent() == null
				&& clonedTree.getRank() == null
				&& isSafeToDelete(clonedTree)) {
			    if (!getObject().delete()) {
				return false;
			    }
			    result = result && clonedTree.deleteAndDissociate();
			}
		    }
		} else {
		    return false;
		}
	    } else {
		getReference().setParent(null);
		getReference().setRank(null);
		result = getReference().save();
	    }
	    if (result) {
		if (getParent() != null) {
		    getParent().deleteChild(this);
		}
		return true;
	    } else {
		return false;
	    }
	} catch (PersistentException ex) {
	    ex.printStackTrace();
	}
	return false;

    }

    @Override
    public DynamicMapDesignTreeNode getParent() {
	return (DynamicMapDesignTreeNode) super.getParent();
    }

    /**
     * Returns true if Tree Reference has no clones
     * 
     * @param temp
     *            - Tree Reference
     * @return true if Tree Reference has no clones
     * @throws NullPointerException
     */
    private static boolean isSafeToDelete(T_dmap_designtree temp) {
	T_dmap_designtree[] clones = temp.t_dmap_designtree1.toArray();
	if (clones != null && clones.length > 0) {
	    return false;
	} else {
	    return true;
	}
    }

    /**
     * Returns true if the Tree Reference has no clones
     * 
     * @return true if Tree Reference of this Node has no clones
     * @throws NullPointerException
     */
    public Boolean isSafeToDelete() {
	return isSafeToDelete(getReference());
    }

    @Override
    public String toString() {
	StringBuffer buffer = new StringBuffer();
	if (this.isClone()) {
	    buffer.append("CLONE CLONE CLONE\n");
	} else {
	    buffer.append("REAL\n");
	}
	buffer.append("Design Tree Id:" + this.getId() + "\n");
	buffer.append(getObject());
	buffer.append("\n" + getValidationErrors());
	return buffer.toString();
    }

    /**
     * Returns Previous Sibling
     * 
     * @return Previous Sibling
     */
    public DynamicMapDesignTreeNode getPrevSibling() {
	if (getParent() != null) {
	    return getParent().getPrevChild(this);
	} else {
	    return null;
	}
    }

    /**
     * Returns Next Sibling
     * 
     * @return Next Sibling
     */
    public DynamicMapDesignTreeNode getNextSibling() {
	if (getParent() != null) {
	    return getParent().getNextChild(this);
	} else {
	    return null;
	}
    }

    /**
     * Returns the child right after the given child
     * 
     * @param child
     *            - reference child
     * @return child right after the given child
     */
    public DynamicMapDesignTreeNode getNextChild(DynamicMapDesignTreeNode child) {
	Integer spot = findChild(child);
	if (spot != null && spot + 1 < this.getChildrenLinkedList().size()) {
	    return (DynamicMapDesignTreeNode) this.getChildrenLinkedList().get(
		    spot + 1);
	}
	return null;
    }

    /**
     * Returns the child right before the given child
     * 
     * @param child
     *            - reference child
     * @return child right before the given child
     */
    public DynamicMapDesignTreeNode getPrevChild(DynamicMapDesignTreeNode child) {
	Integer spot = findChild(child);
	if (spot != null && spot - 1 >= 0) {
	    return (DynamicMapDesignTreeNode) this.getChildrenLinkedList().get(
		    spot - 1);
	}
	return null;
    }

    /**
     * Returns the place of the given child in the children list
     * 
     * @param child
     *            - child to be found
     * @return place of the given child in the children list
     */
    synchronized private Integer findChild(DynamicMapDesignTreeNode child) {
	if (child != null) {
	    DynamicMapDesignTreeNode[] children = getChildren();
	    for (int i = 0; i < children.length; i++) {
		if (children[i].getId() == child.getId()) {
		    return i;
		}
	    }
	}
	return null;
    }

    private int compare(Comparable source) {
	DynamicMapDesignTreeNode src = (DynamicMapDesignTreeNode) source;
	if (!src.getName().equals(getName())) {
	    return Comparable.DIFFERENT_NAME;
	}
	if (!src.getTypeName().equals(getTypeName())) {
	    return Comparable.DIFFERENT_TYPE;
	}
	return getObject().compare(source, this);
    }

    @Override
    public int compare(Comparable source, Comparable treeReference) {
	return compare(source);
    }

    @Override
    public boolean compareParameters(HashMap<String, Object> params,
	    Comparable treeTreference) {
	String name = (String) params.get("name");
	if (!name.equals(getName())) {
	    return false;
	}
	return getObject().compareParameters(params, this);
    }

    @Override
    public String getTypeName() {
	if (getObject() != null) {
	    return getObject().getTypeName();
	} else {
	    return null;
	}
    }

    public boolean hasSameRunTimeParams(HashMap<String, Object> params,
	    Comparable treeTreference) {
	return getObject().hasSameRunTimeParams(params, this);
    }

    /**
     * @return the m_errors
     */
    public String getValidationErrors() {
	return m_errors.toString();
    }

    /**
     * @param m_errors
     *            the m_errors to set
     */
    public void appendValidationError(String errors) {
	m_errors.append(errors + "\r\n");
    }

    public void clearValidationErrors() {
	m_errors = new StringBuilder();
    }

    public boolean isValid() {
	if (m_errors.toString().length() > 0) {
	    return false;
	}
	return true;
    }

    public void validate(DynamicMapDesignTreeNode root) {
	IValidateDynamicMapNode validationObject = ValidationFactory
		.getValidationMethod(this);
	clearValidationErrors();
	validationObject.validate(this, root);
	for (int i = 0; i < this.getChildrenCount(); i++) {
	    this.getChild(i).validate(root);
	}
    }

    @Override
    protected int getChildIndex(TreeNode node) {
	int index = -1;
	DynamicMapDesignTreeNode dNode = (DynamicMapDesignTreeNode) node;
	for (int i = 0; i < getChildrenLinkedList().size(); i++) {
	    DynamicMapDesignTreeNode child = (DynamicMapDesignTreeNode) getChildrenLinkedList()
		    .get(i);
	    if (child.getId() == dNode.getId()) {
		index = i;
		break;
	    }
	}
	return index;
    }

    @Override
    protected boolean isContainsChild(TreeNode node) {
	int index = getChildIndex(node);
	if (index != -1) {
	    return true;
	} else {
	    return false;
	}
    }

    @Override
    public void deleteChild(TreeNode node) {
	if (node != null && node.getParent().equals(this)) {
	    DynamicMapDesignTreeNode dNode = (DynamicMapDesignTreeNode) node;
	    for (int i = 0; i < getChildrenLinkedList().size(); i++) {
		DynamicMapDesignTreeNode child = (DynamicMapDesignTreeNode) getChildrenLinkedList()
			.get(i);
		if (child.getId() == dNode.getId()) {
		    getChildrenLinkedList().remove(i);
		    break;
		}
	    }
	    node.setParent(null);
	}
    }
}
